<template>
  <div class="containers">
    <el-button @click="saveXml">下载XML</el-button>
    <el-button @click="saveSVG">下载SVG</el-button>
    <el-button @click="showXml">预览xml</el-button>
    <div class="canvas" ref="canvas"/>
    <div class="property-panel" ref="propertyPanel">
      <el-form :inline="true" :model="form" label-width="100px" size="small">
        <el-form-item label="节点ID">
          <el-input v-model="form.id" disabled></el-input>
        </el-form-item>
        <el-form-item label="节点名称">
          <el-input v-model="form.name" @input="nameChange"></el-input>
        </el-form-item>
        <el-form-item label="节点颜色">
          <el-color-picker v-model="form.color" @active-change="colorChange"></el-color-picker>
        </el-form-item>
        <!-- 任务节点允许选择人员 -->
        <el-form-item label="节点人员" v-if="userTask">
          <el-select v-model="form.userType" placeholder="请选择" @change="typeChange">
            <el-option value="assignee" label="指定人员"></el-option>
            <el-option value="candidateUsers" label="候选人员"></el-option>
            <el-option value="candidateGroups" label="角色/岗位"></el-option>
          </el-select>
        </el-form-item>
        <!-- 指定人员 -->
        <el-form-item label="指定人员" v-if="userTask && form.userType === 'assignee'">
          <el-select
              v-model="form.assignee"
              placeholder="请选择"
              key="1"
              @change="(value) => addUser({assignee: value})"
          >
            <el-option
                v-for="item in users"
                :key="item.value"
                :label="item.label"
                :value="item.value"
            ></el-option>
          </el-select>
        </el-form-item>
        <!-- 候选人员 -->
        <el-form-item label="候选人员" v-else-if="userTask && form.userType === 'candidateUsers'">
          <el-select
              v-model="form.candidateUsers"
              placeholder="请选择"
              key="2"
              multiple
              @change="(value) => addUser({candidateUsers: value.join(',') || value})"
          >
            <el-option
                v-for="item in users"
                :key="item.value"
                :label="item.label"
                :value="item.value"
            ></el-option>
          </el-select>
        </el-form-item>
        <!-- 角色/岗位 -->
        <el-form-item label="角色/岗位" v-else-if="userTask && form.userType === 'candidateGroups'">
          <el-select
              v-model="form.candidateGroups"
              placeholder="请选择"
              @change="(value) => addUser({candidateGroups: value})"
          >
            <el-option
                v-for="item in roles"
                :key="item.value"
                :label="item.label"
                :value="item.value"
            ></el-option>
          </el-select>
        </el-form-item>
        <!-- 分支允许添加条件 -->
        <el-form-item label="分支条件" v-if="sequenceFlow">
          <el-select v-model="form.user" placeholder="请选择" @change="updateFormalExpression">
            <el-option
                v-for="item in users"
                :key="item.value"
                :label="item.label"
                :value="item.value"
            ></el-option>
          </el-select>
        </el-form-item>
      </el-form>
    </div>
  </div>
  <!--  <div>-->
  <!--    <el-dialog v-model="show">-->
  <!--      <el-input type="textarea" v-model="xml" :rows="30"></el-input>-->
  <!--    </el-dialog>-->
  <!--  </div>-->
  <div>
    <el-drawer title="右侧弹出" v-model="show" direction="rtl">
      <el-input type="textarea" v-model="xml" :rows="35"></el-input>
    </el-drawer>
  </div>
  <div>
    <el-drawer title="流程信息" v-model="showProcess" direction="btt">
      流程名称：
      <el-input type="text" v-model="processForm.name" @change="changeProcessName"></el-input>
      流程描述：
      <el-input type="text" v-model="processForm.documentation" @change="changeProcessDocumentation"></el-input>
    </el-drawer>
  </div>
</template>

<script>
import {toRaw} from "vue";
import BpmnModeler from 'bpmn-js/lib/Modeler'

// import BpmData from "./BpmData";
import customTranslate from "@/customTranslate/customTranslate";


let bpmnModeler = null;
export default {
  data() {
    return {
      element: 1,
      root: null,
      // bpmData: new BpmData(),
      form: {
        id: "",
        name: "",
        color: null,
        user: '',
      },
      show: false,
      showProcess: false,
      xml: '',
      users: [
        {
          value: "zhangsan",
          label: "张三"
        },
        {
          value: "lisi",
          label: "李四"
        },
        {
          value: "wangwu",
          label: "王五"
        }
      ],
      roles: [
        {
          value: "manager",
          label: "经理"
        },
        {
          value: "personnel",
          label: "人事"
        },
        {
          value: "charge",
          label: "主管"
        }
      ],
      processForm: {
        name: '',
        documentation: '',
      }
    };
  },
  computed: {
    userTask() {
      if (!this.element) {
        return;
      }
      return this.element.type === "bpmn:UserTask";
    },
    sequenceFlow() {
      if (!this.element) {
        return;
      }
      return this.element.type === "bpmn:SequenceFlow";
    }
  },
  methods: {
    createNewDiagram() {
      const bpmnXmlStr = `
     <?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="sample-diagram" targetNamespace="http://bpmn.io/schema/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
  <bpmn2:process id="process1567044459787" name="流程1567044459787" key="45353">
    <bpmn2:documentation>描述666</bpmn2:documentation>
    <bpmn2:startEvent id="StartEvent_01ydzqe" name="开始">
      <bpmn2:outgoing>SequenceFlow_1qw929z</bpmn2:outgoing>
    </bpmn2:startEvent>
    <bpmn2:sequenceFlow id="SequenceFlow_1qw929z" sourceRef="StartEvent_01ydzqe" targetRef="Task_1piqdk6" />
    <bpmn2:userTask id="Task_1piqdk6" name="请假申请">
      <bpmn2:incoming>SequenceFlow_1qw929z</bpmn2:incoming>
      <bpmn2:outgoing>SequenceFlow_11h4o22</bpmn2:outgoing>
    </bpmn2:userTask>
    <bpmn2:exclusiveGateway id="ExclusiveGateway_0k39v3u" name="判定条件">
      <bpmn2:incoming>SequenceFlow_11h4o22</bpmn2:incoming>
      <bpmn2:outgoing>SequenceFlow_1iu7pfe</bpmn2:outgoing>
      <bpmn2:outgoing>SequenceFlow_04uqww2</bpmn2:outgoing>
    </bpmn2:exclusiveGateway>
    <bpmn2:sequenceFlow id="SequenceFlow_11h4o22" sourceRef="Task_1piqdk6" targetRef="ExclusiveGateway_0k39v3u" />
    <bpmn2:sequenceFlow id="SequenceFlow_1iu7pfe" sourceRef="ExclusiveGateway_0k39v3u" targetRef="Task_10fqcwp" />
    <bpmn2:userTask id="Task_10fqcwp" name="经理审批" candidateGroups="manager" userType="candidateGroups">
      <bpmn2:incoming>SequenceFlow_1iu7pfe</bpmn2:incoming>
      <bpmn2:outgoing>SequenceFlow_1xod8nh</bpmn2:outgoing>
    </bpmn2:userTask>
    <bpmn2:sequenceFlow id="SequenceFlow_04uqww2" sourceRef="ExclusiveGateway_0k39v3u" targetRef="Task_15n23yh" />
    <bpmn2:userTask id="Task_15n23yh" name="总部审批">
      <bpmn2:incoming>SequenceFlow_04uqww2</bpmn2:incoming>
      <bpmn2:outgoing>SequenceFlow_0c8wrs4</bpmn2:outgoing>
    </bpmn2:userTask>
    <bpmn2:exclusiveGateway id="ExclusiveGateway_1sq33g6">
      <bpmn2:incoming>SequenceFlow_0c8wrs4</bpmn2:incoming>
      <bpmn2:incoming>SequenceFlow_1xod8nh</bpmn2:incoming>
      <bpmn2:outgoing>SequenceFlow_0h8za82</bpmn2:outgoing>
    </bpmn2:exclusiveGateway>
    <bpmn2:sequenceFlow id="SequenceFlow_0c8wrs4" sourceRef="Task_15n23yh" targetRef="ExclusiveGateway_1sq33g6" />
    <bpmn2:sequenceFlow id="SequenceFlow_1xod8nh" sourceRef="Task_10fqcwp" targetRef="ExclusiveGateway_1sq33g6" />
    <bpmn2:endEvent id="EndEvent_0pnmjd3">
      <bpmn2:incoming>SequenceFlow_0h8za82</bpmn2:incoming>
    </bpmn2:endEvent>
    <bpmn2:sequenceFlow id="SequenceFlow_0h8za82" sourceRef="ExclusiveGateway_1sq33g6" targetRef="EndEvent_0pnmjd3" />
  </bpmn2:process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="process1567044459787">
      <bpmndi:BPMNShape id="StartEvent_01ydzqe_di" bpmnElement="StartEvent_01ydzqe">
        <dc:Bounds x="532" y="72" width="36" height="36" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="539" y="53" width="22" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="UserTask_1qxjy46_di" bpmnElement="Task_1piqdk6">
        <dc:Bounds x="500" y="150" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="ExclusiveGateway_0k39v3u_di" bpmnElement="ExclusiveGateway_0k39v3u" isMarkerVisible="true">
        <dc:Bounds x="525" y="275" width="50" height="50" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="528" y="332" width="45" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="UserTask_1j0us24_di" bpmnElement="Task_15n23yh">
        <dc:Bounds x="380" y="380" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="ExclusiveGateway_1sq33g6_di" bpmnElement="ExclusiveGateway_1sq33g6" isMarkerVisible="true">
        <dc:Bounds x="525" y="515" width="50" height="50" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="EndEvent_0pnmjd3_di" bpmnElement="EndEvent_0pnmjd3">
        <dc:Bounds x="532" y="602" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="UserTask_18pwui1_di" bpmnElement="Task_10fqcwp">
        <dc:Bounds x="690" y="380" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="SequenceFlow_1qw929z_di" bpmnElement="SequenceFlow_1qw929z">
        <di:waypoint x="550" y="108" />
        <di:waypoint x="550" y="150" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="SequenceFlow_11h4o22_di" bpmnElement="SequenceFlow_11h4o22">
        <di:waypoint x="550" y="230" />
        <di:waypoint x="550" y="275" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="SequenceFlow_1iu7pfe_di" bpmnElement="SequenceFlow_1iu7pfe">
        <di:waypoint x="575" y="300" />
        <di:waypoint x="740" y="300" />
        <di:waypoint x="740" y="380" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="SequenceFlow_04uqww2_di" bpmnElement="SequenceFlow_04uqww2">
        <di:waypoint x="525" y="300" />
        <di:waypoint x="430" y="300" />
        <di:waypoint x="430" y="380" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="SequenceFlow_0c8wrs4_di" bpmnElement="SequenceFlow_0c8wrs4">
        <di:waypoint x="430" y="460" />
        <di:waypoint x="430" y="540" />
        <di:waypoint x="525" y="540" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="SequenceFlow_1xod8nh_di" bpmnElement="SequenceFlow_1xod8nh">
        <di:waypoint x="740" y="460" />
        <di:waypoint x="740" y="540" />
        <di:waypoint x="575" y="540" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="SequenceFlow_0h8za82_di" bpmnElement="SequenceFlow_0h8za82">
        <di:waypoint x="550" y="565" />
        <di:waypoint x="550" y="602" />
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn2:definitions>
      `;
      bpmnModeler.importXML(bpmnXmlStr);
      // this.adjustPalette();
    },
    // 调整左侧工具栏排版
    adjustPalette() {
      try {
        // 获取 bpmn 设计器实例
        const canvas = this.$refs.canvas;
        const djsPalette = canvas.children[0].children[1].children[5];
        const djsPalStyle = {
          width: "130px",
          padding: "5px",
          background: "white",
          left: "20px",
          borderRadius: 0
        };
        for (let key in djsPalStyle) {
          djsPalette.style[key] = djsPalStyle[key];
        }
        const palette = djsPalette.children[0];
        const allGroups = palette.children;
        allGroups[0].style["display"] = "none";
        // 修改控件样式
        for (let gKey in allGroups) {
          const group = allGroups[gKey];
          for (let cKey in group.children) {
            const control = group.children[cKey];
            const controlStyle = {
              display: "flex",
              justifyContent: "flex-start",
              alignItems: "center",
              width: "100%",
              padding: "5px"
            };
            if (
                control.className &&
                control.dataset &&
                control.className.indexOf("entry") !== -1
            ) {
              const controlProps = this.bpmData.getControl(
                  control.dataset.action
              );
              control.innerHTML = `<div style='font-size: 14px;font-weight:500;margin-left:15px;'>${
                  controlProps["title"]
              }</div>`;
              for (let csKey in controlStyle) {
                control.style[csKey] = controlStyle[csKey];
              }
            }
          }
        }
      } catch (e) {
        console.log(e);
      }
    },
    handleModeler() {
      // 监听节点选择变化
      bpmnModeler.on("selection.changed", e => {
        const element = e.newSelection[0];
        if (!element) {
          return;
        }
        this.element = element;
        this.form = {
          ...element.businessObject,
          ...element.businessObject.$attrs
        };
        if (this.form.userType === "candidateUsers") {
          this.form["candidateUsers"] =
              this.form["candidateUsers"].split(",") || [];
        }
      });

      //  监听节点属性变化
      bpmnModeler.on("element.changed", e => {
        const {element} = e;
        if (!element) return;
        //  新增节点需要更新回属性面板
        if (element.id === this.form.id) {
          this.form.name = element.businessObject.name;
          this.form = {...this.form};
        }
      });
      bpmnModeler.on("element.click", e => {
        const {element} = e;
        if (e.element.type == 'bpmn:Process') {
          this.root = e
          this.showProcess = true

          this.processForm.name = e.element.businessObject.name
          this.processForm.documentation = e.element.businessObject.documentation[0].text
          console.log(element)
        }
      });
    },
    // 属性面板名称，更新回流程节点
    nameChange(name) {
      const modeling = bpmnModeler.get("modeling");
      modeling.updateLabel(toRaw(this.element), name);
    },
    // 属性面板颜色，更新回流程节点
    colorChange(color) {
      const modeling = bpmnModeler.get("modeling");
      modeling.setColor(toRaw(this.element), {
        fill: null,
        stroke: color
      });
      modeling.updateProperties(toRaw(this.element), {color: color});
    },
    // 任务节点配置人员
    addUser(properties) {
      this.updateProperties(
          Object.assign(properties, {
            userType: Object.keys(properties)[0]
          })
      );
    },
    // 切换人员类型
    typeChange() {
      const types = ["assignee", "candidateUsers", "candidateGroups"];
      types.forEach(type => {
        delete this.element.businessObject.$attrs[type];
        delete this.form[type];
      });
    },
    // 在这里我们封装一个通用的更新节点属性的方法
    updateProperties(properties) {
      const modeling = bpmnModeler.get("modeling");
      modeling.updateProperties(toRaw(this.element), properties);
    },
    updateFormalExpression() {
      const modeling = bpmnModeler.get("modeling");
      const _moddle = bpmnModeler._moddle;
      const conditionExpression = _moddle.create("bpmn:FormalExpression", {
        body: this.form.user,
      });
      modeling.updateProperties(toRaw(this.element), {
        conditionExpression: conditionExpression
      });
    },

    changeProcessName() {
      const modeling = bpmnModeler.get("modeling");
      modeling.updateModdleProperties(this.root, this.root.element.businessObject, {name: this.processForm.name});
    },
    changeProcessDocumentation() {
      const modeling = bpmnModeler.get("modeling");
      modeling.updateModdleProperties(this.root, this.root.element.businessObject.documentation[0], {text: this.processForm.documentation});
    },
    saveXml() {
      try {
        bpmnModeler.saveXML({format: true}).then(res => {
          const {xml} = res;
          let xmlBlob = new Blob([xml], {
            type: "application/bpmn20-xml;charset=UTF-8,"
          });
          let downloadLink = document.createElement("a");
          downloadLink.download = "ops-coffee-bpmn.bpmn";
          downloadLink.innerHTML = "Get BPMN SVG";
          downloadLink.href = window.URL.createObjectURL(xmlBlob);
          downloadLink.onclick = function (event) {
            document.body.removeChild(event.target);
          };
          downloadLink.style.visibility = "hidden";
          document.body.appendChild(downloadLink);
          downloadLink.click();
        });
      } catch (err) {
        console.log(err);
      }
    },
    saveSVG() {
      try {
        bpmnModeler.saveSVG().then(res => {
          const {svg} = res;
          let svgBlob = new Blob([svg], {
            type: "image/svg+xml"
          });
          let downloadLink = document.createElement("a");
          downloadLink.download = "ops-coffee-bpmn.svg";
          downloadLink.innerHTML = "Get BPMN SVG";
          downloadLink.href = window.URL.createObjectURL(svgBlob);
          downloadLink.onclick = function (event) {
            document.body.removeChild(event.target);
          };
          downloadLink.style.visibility = "hidden";
          document.body.appendChild(downloadLink);
          downloadLink.click();
        });
      } catch (err) {
        console.log(err);
      }
    },
    showXml() {
      this.xml = ''
      this.show = true
      bpmnModeler.saveXML({format: true}).then(res => {
        this.xml = res.xml
      })
    }
  },
  mounted() {
    const canvas = this.$refs.canvas;
    let customTranslateModule = {
      translate: ['value', customTranslate]
    }
    // 生成实例
    bpmnModeler = new BpmnModeler({
      container: canvas,
      additionalModules: [customTranslateModule],
    });
    this.handleModeler();
    this.createNewDiagram();
  }
}
</script>
<style lang="css">
/*左边工具栏以及编辑节点的样式*/
@import "bpmn-js/dist/assets/diagram-js.css";
@import "bpmn-js/dist/assets/bpmn-font/css/bpmn.css";
@import "bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css";
@import "bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css";

.containers {
  position: absolute;
  background-color: #ffffff;
  width: 100%;
  height: 100%;

  .canvas {
    width: 100%;
    height: 100%;
  }

  .panel {
    position: absolute;
    right: 0;
    top: 50px;
    width: 300px;
  }

  .bjs-powered-by {
    display: none;
  }

  .toolbar {
    position: absolute;
    top: 20px;
    right: 350px;

    a {
      text-decoration: none;
      margin: 5px;
      color: #409eff;
    }
  }
}

.property-panel {
  position: absolute;
  right: 0px;
  top: 0px;
  border-left: 1px solid #cccccc;
  padding: 20px 0;
  width: 300px;
  height: 100%;
}
</style>
